﻿using EFCoreDemo.Entitys;
using EFCoreDemo.IRepositorys;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System;
using System.Collections;
using System.Linq;
using System.Threading.Tasks;

namespace EFCoreDemo.Repositorys
{
    public class UnitOfWork : IUnitOfWork
    {
        /// <summary>
        /// 上下文对象，UnitOfWork内部初始化上下文对象，供当前scope内的操作使用，保证同一上下文
        /// </summary>
        private DbContext DbContext { get; set; }

        private IDbContextTransaction _dbTransaction { get; set; }

        /// <summary>
        /// 当前请求涉及的scope生命的仓储对象
        /// </summary>
        private Hashtable repositorys;

        public UnitOfWork(DbContext blogContext)
        {
            DbContext = blogContext;
        }

        public IBaseRepository<TEntity> Repository<TEntity>() where TEntity : class,new()
        {
            if (repositorys == null)
                repositorys = new Hashtable();

            var entityType = typeof(TEntity);
            if (!repositorys.ContainsKey(entityType.Name))
            {
                var baseType = typeof(BaseRepository<>);
                var repositoryInstance = Activator.CreateInstance(baseType.MakeGenericType(entityType), DbContext);
                repositorys.Add(entityType.Name, repositoryInstance);
            }
            return (IBaseRepository<TEntity>)repositorys[entityType.Name];
        }


        #region 开启事务
        public void BeginTransaction()
        {
            _dbTransaction = DbContext.Database.BeginTransaction();
        }
        #endregion

        #region 提交
        public int Commit()
        {
            int result = 0;
            try
            {
                result = DbContext.SaveChanges();
                if (_dbTransaction != null)
                    _dbTransaction.Commit();
            }
            catch (Exception ex)
            {
                result = -1;
                CleanChanges(DbContext);
                _dbTransaction.Rollback();
                throw new Exception($"Commit 异常：{ex.InnerException}/r{ ex.Message}");
            }
            finally
            {
                if (_dbTransaction != null)
                    _dbTransaction = null;
            }
            return result;
        }

        public async Task<int> CommitAsync()
        {
            int result = 0;
            try
            {
                result = await DbContext.SaveChangesAsync();
                if (_dbTransaction != null)
                    _dbTransaction.Commit();
            }
            catch (Exception ex)
            {
                result = -1;
                CleanChanges(DbContext);
                _dbTransaction.Rollback();
                throw new Exception($"Commit 异常：{ex.InnerException}/r{ ex.Message}");
            }
            finally
            { 
                if (_dbTransaction != null)
                    _dbTransaction = null;
            }
            return await Task.FromResult(result);
        }
        #endregion 

        #region override
        public void Dispose()
        {
            if (_dbTransaction != null)
                _dbTransaction.Dispose();
            DbContext.Dispose();
            GC.SuppressFinalize(this);
        }
        #endregion

        #region private 
        #region 操作失败，还原跟踪状态
        /// <summary>
        ///  操作失败，还原跟踪状态
        /// </summary>
        /// <param name="context"></param>
        private static void CleanChanges(DbContext context)
        {
            var entries = context.ChangeTracker.Entries().ToArray();
            for (int i = 0; i < entries.Length; i++)
            {
                entries[i].State = EntityState.Detached;
            }
        }
        #endregion 
        #endregion
    }
}
